// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef CLOUDAPPENGINE_CAETIMER_H
#define CLOUDAPPENGINE_CAETIMER_H

#include <ctime>
#include <vector>
#include <sys/prctl.h>
#include <linux/prctl.h>
#include "CaeThread.h"
#include "CaeLogConfig.h"

class CaeTimer {
public:
    uint32_t m_interval;
    struct timeval m_lasttime {};
    uint32_t m_timerid;
    explicit CaeTimer(uint32_t interval)
    {
        m_timerid = 0;
        m_interval = interval;
        memset_s(&m_lasttime, sizeof(m_lasttime), 0, sizeof(m_lasttime));
    }

    virtual ~CaeTimer() {}

    virtual void TimeoutCallback() = 0;

    virtual void Reset()
    {
        gettimeofday(&m_lasttime, nullptr);
    }

    void SetTimerId(uint32_t timeid)
    {
        m_timerid = timeid;
    }
};

class CaeTimerManage {
public:
    std::vector<CaeTimer *> m_tVec;
    uint32_t m_tCount;

    static CaeTimerManage &GetInstance();
    CaeTimerManage(const CaeTimerManage &) = delete;
    CaeTimerManage &operator = (const CaeTimerManage &) = delete;

    pthread_mutex_t m_lock;

    CaeTimerManage()
    {
        m_tCount = 1;
        pthread_mutex_init(&m_lock, nullptr);
    }

    ~CaeTimerManage()
    {
        DeleteAll();
        pthread_mutex_destroy(&m_lock);
    }

    void DeleteAll() noexcept
    {
        pthread_mutex_lock(&m_lock);
        for (auto timer : m_tVec) {
            delete timer;
            timer = nullptr;
        }
        m_tVec.clear();
        pthread_mutex_unlock(&m_lock);
        LOGD("CaeTimerManage DeleteAll. ");
    }

    void AddTimer(CaeTimer *timer)
    {
        pthread_mutex_lock(&m_lock);
        timer->SetTimerId(m_tCount);
        m_tVec.push_back(timer);
        m_tCount++;
        m_tCount = m_tCount % 100;
        LOGD("Add timer id=%d. ", timer->m_timerid);
        timer->Reset();
        pthread_mutex_unlock(&m_lock);
    }

    void DeleteTimer(CaeTimer *timer) noexcept
    {
        pthread_mutex_lock(&m_lock);
        std::vector<CaeTimer *>::iterator it;
        for (it = m_tVec.begin(); it != m_tVec.end();) {
            if (*it == timer) {
                LOGI("delete timer id=%d", timer->m_timerid);
                it = m_tVec.erase(it);
            } else {
                ++it;
            }
        }
        pthread_mutex_unlock(&m_lock);
    }

    void Loop()
    {
        pthread_mutex_lock(&m_lock);
        if (m_tVec.size() > 0) {
            struct timeval curTime {};
            gettimeofday(&curTime, nullptr);
            for (uint32_t i = 0; i < m_tVec.size(); ++i) {
                CaeTimer *timer = m_tVec.at(i);
                if (timer == nullptr) {
                    LOGE("CaeTimerManage bad timer");
                    pthread_mutex_unlock(&m_lock);
                    return;
                }
                long long diff_sec = curTime.tv_sec - timer->m_lasttime.tv_sec;
                long long diff_usec = curTime.tv_usec - timer->m_lasttime.tv_usec;
                long long diff = diff_sec * 1000 + diff_usec / 1000;
                if (diff < 0) {
                    LOGE("CaeTimerManage bad timer diff:%lld--cur_time:%lld:%lld--m_lasttime:%lld:%lld", diff,
                         curTime.tv_sec, curTime.tv_usec, timer->m_lasttime.tv_sec, timer->m_lasttime.tv_usec);
                    continue;
                }
                if (diff > timer->m_interval) {
                    timer->TimeoutCallback();
                    timer->Reset();
                }
            }
        }
        pthread_mutex_unlock(&m_lock);
    }
};


class CaeTimerThread : public CaeThread {
public:
    CaeTimerThread();

    ~CaeTimerThread() override;

    void Run() override;
};


#endif // CLOUDAPPENGINE_CAETIMER_H
